昨天介紹了在Dart中非同步的基本概念,今天就要來講到如何簡單的控制非同步操作。
Future 可以想像成一個盒子一樣,它將 「一個值裝起來直到未來某個時間點才會打開」
直接看code,我們宣告了一個function他會回傳一個 Future<String> ,同時我也會看到 Future 最基本的 constructor Future(FutureOr<T> computation())
所以這邊我們先傳入一個會回傳String的 Function
Future<String> fetchData() => Future(
() => 'Data',
);
然後到main裡使用:
final data = fetchData();
print(data);
會發現輸出不是 Data 而是 Instance of 'Future<String>' 為什麼呢?就像前面所說的Future 可以想像成一個盒子一樣,所以我們必須將它打開才能取出它的值。
而打開這個盒子的其中一種方法就是用 then
fetchData().then((value) => print(value));
// Data
.then 會回傳一個callback 然後我們就可以用 (value) => print(value) 這種形式來使用它,那如果我的非同步是有一連串的順序呢?
假設我想要非同步的取得一個資料後,再經過三秒後才輸出的話
一樣先定義兩個回傳 Future<String> 的 function
Future<String> outputAfter3s(String data) => Future.delayed(
Duration(seconds: 3),
() => data,
);
Future<String> fetchData() => Future(
() => 'data',
);
在使用上就直接插入一個 .then 就是這麼簡單
fetchData()
.then(
(value) => outputAfter3s(value),
)
.then((value) => print(value))
整個流程大概如下:
outputAfter3s 回傳一個 Future 後,一樣是一個FutureOr<T> computation() 所以可以繼續往下接.then ,這個FutureOr 意思是可能是 T 或者 Future<T> 。
所以其實也可以直接傳一個value下去
fetchData()
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
// 4
Future 另外一個好處是可以讓我們更方便的catch error
Future throwError() => Future(
() => throw 'error 123456',
);
// 省略其他code ...
fetchData()
.then((value) => throwError())
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
.catchError(
(err) => print('catch error: $err'),
);
我們這邊先直接宣告一個一定會throw error的function,當然在實務上常見的可能會是http client發生一些錯誤才會throw error。
然後我們利用 catchError 來做錯誤處理,使用方式也很簡單,它一樣會回傳一個 callback 裡面可以取得這次非同步中throw error。
所以會有以下輸出
catch error: error 123456
如果我有一些操作是想要整個Future chain 都結束後且 「無論失敗或成功」 都要執行的話那我該如何寫?
fetchData()
.then((value) => throwError())
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
.catchError(
(err) => print('catch error: $err'),
)
.whenComplete(() => print('completed'));
可以使用 whenComplete 這個api 來達成而且他是 「無論失敗或成功」 只要這個 Future有了結果回傳都會執行。
所以輸出會是這樣
catch error: error 123456
completed
如果沒有throw error:
4
completed
所以我們現在可以得知future有三種狀態 「成功」、「失敗」、「未完成」 ,但為什麼沒有講解到 「未完成」 (pending)時候的控制呢?主要是因為這件事情有了UI才會比較需要呈現。所以等之後進入到flutter的文章時再來慢慢介紹要如何實作分別呈現 「成功」、「失敗」、「未完成」 這三種UI。
這次講了算是相當常見的 Future ,但其實我們在絕大多數的場景下很少自己建立Future ,大多數時候都是第三方服務會回傳Future 來讓我們做控制像是http request 之類的。
但你可能會想問難道我只能從 then 裡面才能將值取出來嗎?如果我有將Future 裡的值取出來放到另外一個變數該如何做?其實如果有state存在的話,是可以在Future chain裡去將state改變。但大多數的做法應該都會是利用 async/await 來達成這件事情,就等到明天再來好好說明async/await 了。
今天的程式碼:
https://github.com/zxc469469/dart-playground/tree/Day09/future
參考資料
https://www.youtube.com/watch?v=OTS-ap9_aXc